﻿using System;
using System.Collections.Generic;
using FunctionalCLR.Core;
using FunctionalCLR.Core.Function.Arithmetics;
using VIJAY.MATH;

namespace Zove.Runtime.InferencingStrategy
{
    public class SearchResult
    {
        public RuntimeEquation Equation
        {
            get;
            set;
        }

        public List<string> Elements
        {
            get;
            set;
        }

        public SearchResult()
        {
            this.Elements = new List<string>();
        }
    }

    public class EquationGroup
    {
        public List<RuntimeEquation> Equations
        {
            get;
            set;
        }

        public List<string> Elements
        {
            get;
            set;
        }

        public EquationGroup()
        {
            this.Elements = new List<string>();
            this.Equations = new List<RuntimeEquation>();
        }

        public override int GetHashCode()
        {
            return base.GetHashCode();
        }

        public EquationGroup(List<RuntimeEquation> eg)
            : this()
        {
            this.Equations = eg;
        }

        public override bool Equals(object obj)
        {
            if (!obj.GetType().Equals(typeof(EquationGroup)))
            {
                return false;
            }

            EquationGroup eg = (EquationGroup)obj;

            foreach (RuntimeEquation e in eg.Equations)
            {
                if (!this.Equations.Contains(e))
                {
                    return false;
                }
            }

            return true;
        }
    }

    public class SearchResultComparer : IComparer<SearchResult>
    {
        #region IComparer<SearchResult> Members

        public int Compare(SearchResult x, SearchResult y)
        {
            return x.Elements.Count.CompareTo(y.Elements.Count);
        }

        #endregion
    }

    public class NonlinearInferencingStrategy : IInferencingStrategy
    {
        private List<SearchResult> results = new List<SearchResult>();

        private Instance sln;

        public double Evaluate(SolvedFactor sf)
        {
            return 0;
        }

        /// <summary>
        /// Evaluates
        /// </summary>
        /// <param name="eg"></param>
        /// <returns></returns>
        public double[] Evaluate(EquationGroup eg)
        {
            List<string> s = new List<string>();
            Dictionary<string, string> rpl = new Dictionary<string, string>();

            foreach (RuntimeEquation e in eg.Equations)
            {
                SubNode node = new SubNode();
                node.Left = e.Left;
                node.Right = e.Right;

                for (int index = 0; index < e.Factors.Count; index++)
                {
                    string x = "x" + index.ToString();
                    rpl[e.Factors[index].Name] = x;
                }

                CSharpExpressionFiller ef = new CSharpExpressionFiller(node);

                string m = ef.Fill();
                m = m + "=0";
                s.Add(m);

                Utils.Debugging("expr: " + m);
            }

            NonLinear.SetSystem(s.ToArray());
            double[] r = NonLinear.NewtonRapson();

            for (int index = 0; index < eg.Elements.Count; index++)
            {
                Utils.Debugging("e: " + eg.Elements[index]);
                //this.sln.Space[eg.Elements[index]] = r[index];
                RuntimeFactor rf = this.sln.Model.GetFactorByName(
                    eg.Elements[index]);
                rf.Value = r[index];
                Utils.Debugging(eg.Elements[index] + " = " + r[index]);
            }

            return r;
        }

        #region IInferencingStrategy Members

        public int Priority()
        {
            throw new NotImplementedException();
        }

        public void Infer(Instance sln)
        {
            this.sln = sln;
            Utils.Debugging("Mat:");
            Utils.Debugging(sln.Model.ToString());
            foreach (RuntimeEquation r in sln.Model.Equations)
            {
                SearchResult result = new SearchResult();
                result.Equation = r;
                results.Add(result);

                foreach (RuntimeFactor e in r.Factors)
                {
                    if(e.Status != RuntimeFactorStatus.Unknown)
                    {
                        result.Elements.Add(e.Name);
                    }
                }
            }

            results.Sort(new SearchResultComparer());

            List<EquationGroup> ses = new List<EquationGroup>();

            foreach (SearchResult r in results)
            {
                EquationGroup eg = new EquationGroup();
                eg.Equations.Add(r.Equation);
                eg.Elements.AddRange(r.Elements);

                foreach (SearchResult v in results)
                {
                    if (r == v)
                    {
                        continue;
                    }

                    if (r.Elements.Count == v.Elements.Count)
                    {
                        bool done = true;
                        foreach (string m in r.Elements)
                        {
                            if (!v.Elements.Contains(m))
                            {
                                done = false;
                                break;
                            }
                        }
                        if (done)
                        {
                            eg.Equations.Add(v.Equation);
                            if (eg.Elements.Count == r.Elements.Count)
                            {
                                ses.Add(eg);
                            }
                        }
                    }
                }
            }

            for (int i = 0; i < ses.Count; i++)
            {
                for (int j = 0; j < ses.Count; j++)
                {
                    if (i == j)
                    {
                        continue;
                    }

                    if (ses[i].Equals(ses[j]))
                    {
                        ses.RemoveAt(j);
                    }
                }
            }

            ShowSet(ses);

            foreach (EquationGroup eg in ses)
            {
                Evaluate(eg);
            }
        }

        #endregion

        public void ShowSet(List<EquationGroup> egs)
        {
            Utils.Debugging("======================");
            foreach (EquationGroup eg in egs)
            {
                foreach (RuntimeEquation e in eg.Equations)
                {
                    Utils.Debugging(e.Equation);
                }
                Utils.Debugging("======================");
            }
        }
    }
}
